package atlantik.game;

import java.awt.Color;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

class AtlantikEventDispatcher {

	private static final int INVALID = Integer.MIN_VALUE;
	private static final int EMPTY = -1;

	private static final String TOKEN_IMAGES = "images/tokens";
	private static final String BOARD_IMAGES = "images/board";
	private static final String DEFAULT_TOKEN = "logo.png";

	private final MediaTracker tracker = new MediaTracker(new java.awt.Canvas());

	private final AtlantikListener listener;
	private final AtlantikState state;
	private final DocumentBuilder parser;

	// constructor
	public AtlantikEventDispatcher(AtlantikState state, AtlantikListener listener) throws ParserConfigurationException {
		this.state = state;
		this.listener = listener;

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		parser = factory.newDocumentBuilder();
	}

	public void dispatch(String line) throws SAXException, InstantiationException {
		if( line == null )
			return;
		listener.xml(line);

		try {
			Document doc = parser.parse(new ByteArrayInputStream(line.getBytes()));
			Element root = doc.getDocumentElement();
			dispatchRoot(root);
		} catch( IOException e ) {
			System.out.println(e);
			e.printStackTrace(System.err);
			return;
		}
	}

	void dispatchRoot(Node node) throws SAXException, InstantiationException {
		checkNode(node, "monopd");

		NodeList children = node.getChildNodes();
		for( int i = 0; i < children.getLength(); i++ )
			dispatchMonopd(children.item(i));	}

	void dispatchMonopd(Node node) throws SAXException, InstantiationException {
		if( isNode(node, "server") )
			dispatchServer(node);
		else if( isNode(node, "msg") )
			dispatchMsg(node);
		else if( isNode(node, "display") )
			dispatchDisplay(node);
		else if( isNode(node, "client") )
			dispatchClient(node);
		else if( isNode(node, "configupdate") )
			dispatchConfigUpdate(node);
		else if( isNode(node, "deletegame") )
			dispatchDeleteGame(node);
		else if( isNode(node, "gameupdate") )
			dispatchGameUpdate(node);
		else if( isNode(node, "updategamelist") )
			dispatchGameListUpdate(node);
		else if( isNode(node, "deleteplayer") )
			dispatchDeletePlayer(node);
		else if( isNode(node, "playerupdate") )
			dispatchPlayerUpdate(node);
		else if( isNode(node, "estategroupupdate") )
			dispatchEstateGroupUpdate(node);
		else if( isNode(node, "estateupdate") )
			dispatchEstateUpdate(node);
		else if( isNode(node, "auctionupdate") )
			dispatchAuctionUpdate(node);
/*
 * else if( isNode(node, "tradeupdate") ) dispatchTradeUpdate(node);
 */
		else if( isNode(node, "cardupdate") )
			return; // ignore
		else if( isNode(node, "#text") )
			return; // ignore
		else {
			throw new SAXException("Unknown tag: " + node.getNodeName());
		}
	}

	void dispatchServer(Node node) throws SAXException {
		NamedNodeMap attrs = node.getAttributes();
		String version = getString(attrs, "version");
		listener.serverVersion(version);
	}


	void dispatchMsg(Node node) throws SAXException {
		NamedNodeMap attrs = node.getAttributes();
		String type = getString(attrs, "type");
		String text = getString(attrs, "value");

		if ( "error".equals(type) )
			listener.errorAdded(text);
		else if ("info".equals(type))
			listener.infoAdded(text);
		else if ("chat".equals(type)) {
			String author = getString(attrs, "author");
			int playerId = getInt(attrs, "playerid");
			listener.chat(playerId, author, text);
		} else
			throw new SAXException("Message is of unknown type");
	}

	void dispatchClient(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int playerId = getInt(attrs, "playerid");
		String cookie = getString(attrs, "cookie");

		Player player = state.players().find(playerId);
		if( player == null )
			player = state.players().create(Player.class, playerId);

		state.setSelf(player);
		listener.clientInfo(playerId, cookie);
	}

	void dispatchPlayerUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int playerId = getInt(attrs, "playerid");
		Player player;
		if( (player = state.players().find(playerId)) == null )
			player = state.players().create(Player.class, playerId);

		// Update player name
		String name = getOptionalString(attrs, "name");
		if( name != null )
			player.name = name;

		// Update player game
		int gameId = getOptionalInt(attrs, "game");
		if(gameId != INVALID) {
			if( gameId == EMPTY )
				player.setGame(null);
			else {
			// Ensure setGame succeeds by creating game if necessary
				Game game = state.games().find(gameId);
				if (game == null)
					game = state.games().create(Game.class, gameId);
				player.setGame(game);
			}
		}

		// Update player host
		String host = getOptionalString(attrs, "host");
		if( host != null )
			player.host = host;


		// Update player image/token
		String image = getOptionalString(attrs, "image");
		if( image != null )
			player.setToken(getImage(TOKEN_IMAGES, image));
		if( player.getToken() == null )
			player.setToken(getImage(TOKEN_IMAGES, DEFAULT_TOKEN));

		// Update player money
		int money = getOptionalInt(attrs, "money");
		if( money != INVALID )
			player.money = money;

		try {
			player.isBankrupt = getBoolean(attrs, "bankrupt");
		} catch( SAXException e ) {}

		try {
			player.hasDebt = getBoolean(attrs, "hasdebt");
		} catch( SAXException e ) {}

		try {
			player.hasTurn = getBoolean(attrs, "hasturn");
		} catch( SAXException e ) {}

		int doubleCount = getOptionalInt(attrs, "doublecount");
		if( doubleCount != INVALID )
			player.doubleCount = doubleCount;

		try {
			player.canRoll = getBoolean(attrs, "can_roll");
		} catch( SAXException e ) {}

		try {
			player.canRollAgain = getBoolean(attrs, "canrollagain");
		} catch( SAXException e ) {}

		try {
			player.canAuction = getBoolean(attrs, "canauction");
		} catch( SAXException e ) {}

		try {
			player.canBuyEstate = getBoolean(attrs, "can_buyestate");
		} catch( SAXException e ) {}

		try {
			player.canAuction = getBoolean(attrs, "canauction");
		} catch( SAXException e ) {}

		try {
			player.canUseCard = getBoolean(attrs, "canusecard");
		} catch( SAXException e ) {}

		try {
			player.isJailed = getBoolean(attrs, "jailed");
		} catch( SAXException e ) {}

		int jailCount = getOptionalInt(attrs, "jailcount");
		if( jailCount != INVALID )
			player.jailCount = jailCount;

		try {
			player.isSpectator = getBoolean(attrs, "spectator");
		} catch( SAXException e ) {}

		try {
			player.setMoving(!getBoolean(attrs, "directmove"));
		} catch( SAXException e ) {}
		
		// Update player location
		int location = getOptionalInt(attrs, "location");
		if(location != INVALID )
		{
			Estate estate = state.estates().find(location);
			if( estate == null ) {
				estate = state.estates().create(Estate.class, location);
				estate.update();	// TODO remove this workaround
			}
			if( estate != null )
				player.setLocation(estate);
		}

		player.update();
	}

	void dispatchGameListUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		String type = getString(attrs, "type");

		if( "full".equals(type) )
			state.games().clear();

		NodeList children = node.getChildNodes();
		for( int i = 0; i < children.getLength(); i++ )
			dispatchGameUpdate(children.item(i));
	}

	void dispatchGameUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		String name = getOptionalString(attrs, "name");
		String description = getOptionalString(attrs, "description");

		int gameId = getOptionalInt(attrs, "gameid");
		if( gameId == INVALID )
			gameId = getOptionalInt(attrs, "id");
		if (gameId == INVALID || gameId == -1)
		{
			String gametype = getString(attrs, "gametype");

			GameChoice choice = state.gameChoices().find(gametype);
			if( choice == null )
				choice = state.gameChoices().create(GameChoice.class, gametype);
			choice.setName(name);
			choice.setDescription(description);
			choice.update();
			return;
		}
		Game game = state.games().find(gameId);

		if( game == null )
			game = state.games().create(Game.class, gameId);

		if( name != null )
			game.setName(name);
		if( description != null )
			game.setDescription(description);

		try {
			game.setCanBeJoined(getBoolean(attrs, "canbejoined"));
		} catch( SAXException e ) {}

		int players = getOptionalInt(attrs, "players");
		if( players != INVALID )
			game.setPlayers(players);

		int minPlayers = getOptionalInt(attrs, "minplayers");
		if( minPlayers != INVALID )
			game.setMinPlayers(minPlayers);

		int maxPlayers = getOptionalInt(attrs, "maxplayers");
		if( maxPlayers != INVALID )
			game.setMaxPlayers(maxPlayers);

		int masterId = getOptionalInt(attrs, "master");
		if ( masterId != INVALID ) {
			Player master = state.players().find( masterId );
			if ( master == null )
				master = state.players().create( Player.class, masterId );
			game.setMaster(master);
		}

		String sStatus = getOptionalString(attrs, "status");
		if( sStatus != null ) {
			Game.Status status = Game.statusFromString(sStatus);
			game.setStatus(status);
			if( status != Game.Status.NONE && status != Game.Status.ENDED )
				state.self().game = game;
		}
		game.update();
	}

	void dispatchConfigUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int gameId = getOptionalInt(attrs, "gameid");
		if( gameId != INVALID ) {
			Game game = state.games().find(gameId);
			if( game == null )
				throw new SAXException("ConfigUpdate did not specify a valid game");

			NodeList children = node.getChildNodes();
			for( int i = 0; i < children.getLength(); i++ )
				dispatchOption(game, children.item(i));
		} else {
			// old style?
			Game game = state.self().getGame();
			if( game == null )
				throw new SAXException("ConfigUpdate did not specify a valid game");

			int id = getInt(attrs, "configid");
			String command = ".gc" + id + ":";

			Game.Option option = game.options().find(command);
			if( option == null ) {
				String type = getString(attrs, "type");
				Class<Game.Option> cls = Game.getOptionClass(type);
				option = game.options().create(cls, command);
			}

			try {
				option.setCanEdit(getBoolean(attrs, "edit"));
			} catch( SAXException e ) {}

			String description = getOptionalString(attrs, "description");
			if( description != null )
				option.setTitle(description);
			String value = getOptionalString(attrs, "value");
			if( value != null )
				option.setValue(value);
				
			option.update();
		}

	}

	void dispatchOption(Game game, Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		checkNode(node, "option");

		String command = getString(attrs, "command");

		Game.Option option = game.options().find(command);
		if( option == null ) {
			String type = getString(attrs, "type");
			Class<Game.Option> cls = Game.getOptionClass(type);
			option = game.options().create(cls, command);
		}

		try {
			option.setCanEdit(getBoolean(attrs, "edit"));
		} catch( SAXException e ) {}

		String description = getOptionalString(attrs, "title");
		if( description != null )
			option.setTitle(description);
		String value = getOptionalString(attrs, "value");
		if( value != null )
			option.setValue(value);

		option.update();
	}

	void dispatchDisplay(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int estateId = getInt(attrs, "estateid");
		Estate estate = state.estates().find(estateId);

		String text = getString(attrs, "text");
		if( getBoolean(attrs, "cleartext") )
			state.details().clear();
		if( getBoolean(attrs, "clearbuttons") ) {
			state.details().actions().clear();
		}

		Detail detail = state.details().create(Detail.class);
		detail.setEstate(estate);
		detail.setText(text);
		detail.update();

		NodeList children = node.getChildNodes();
		for( int i = 0; i < children.getLength(); i++ )
			dispatchButton(children.item(i));
			
	}

	void dispatchButton(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		checkNode(node, "button");
		
		DetailAction action = state.details().actions().create(DetailAction.class);
		
		action.setCommand(getString(attrs, "command"));
		action.setCaption(getString(attrs, "caption"));
		action.setEnabled(getBoolean(attrs, "enabled"));
		action.update();
	}

	void dispatchDeleteGame(Node node) throws SAXException {
		NamedNodeMap attrs = node.getAttributes();

		int gameId = getInt(attrs, "gameid");

		state.games().remove(gameId);
	}

	void dispatchDeletePlayer(Node node) throws SAXException {
		NamedNodeMap attrs = node.getAttributes();

		int playerId = getInt(attrs, "playerid");

		state.players().remove(playerId);
	}

	void dispatchEstateGroupUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int groupId = getInt(attrs, "groupid");

		EstateGroup estateGroup = state.estateGroups().find(groupId);
			estateGroup = state.estateGroups().create(EstateGroup.class, groupId);

		estateGroup.setName(getString(attrs, "name"));

		estateGroup.update();
	}

	void dispatchEstateUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int estateId = getInt(attrs, "estateid");
		Estate estate = state.estates().find(estateId);
		if( estate == null )
			estate = state.estates().create(Estate.class, estateId);

		String name = getOptionalString(attrs, "name");
		if ( name != null )
			estate.setName(name);

		Color color = getOptionalColor(attrs, "color");
		if ( color != null )
			estate.setColor(color);

		Color bgColor = getOptionalColor(attrs, "bgcolor");
		if ( bgColor != null )
			estate.setBackgroundColor(bgColor);

		String icon = getOptionalString(attrs, "icon");
		if ( icon != null )
			estate.setIcon(getImage(BOARD_IMAGES, icon));


		int ownerId = getOptionalInt(attrs, "owner");
		if( ownerId != INVALID ) {
			Player owner = state.players().find(ownerId);
			if( estate.getOwner() != owner ) {
				if( estate.getOwner() != null )
					estate.getOwner().estates.remove(estate);
				estate.setOwner(owner);
				owner.estates.add(estate);
			}
		}

		int houses = getOptionalInt(attrs, "houses");
		if ( houses != INVALID )
			estate.setHouses(houses);

		try {
			estate.setMortgaged(getBoolean(attrs, "mortgaged"));
		} catch( SAXException e ) {}

		int groupId = getOptionalInt(attrs, "group");
		if( groupId != INVALID )
			if( groupId == EMPTY ) {
				estate.setGroup(null);
			} else {
				EstateGroup group = state.estateGroups().find(groupId);
				if( group == null )
					group = state.estateGroups().create(EstateGroup.class, groupId);
				estate.setGroup( group );
				group.add(estate);
			}

		try {
			estate.setCanToggleMortgage(getBoolean(attrs, "can_toggle_mortgage"));
		} catch( SAXException e ) {}

		try {
			estate.setCanBeOwned(getBoolean(attrs, "can_be_owned"));
		} catch( SAXException e ) {}

		try {
			estate.setCanBuyHouses(getBoolean(attrs, "can_buy_houses"));
		} catch( SAXException e ) {}

		try {
			estate.setCanSellHouses(getBoolean(attrs, "can_sell_houses"));
		} catch( SAXException e ) {}


		int price = getOptionalInt(attrs, "price");
		if ( price != INVALID )
			estate.setPrice(price);

		int mortgagePrice = getOptionalInt(attrs, "mortgageprice");
		if ( mortgagePrice != INVALID )
			estate.setMortgagePrice(mortgagePrice);

		int unmortgagePrice = getOptionalInt(attrs, "unmortgageprice");
		if ( unmortgagePrice != INVALID )
			estate.setUnmortgagePrice(unmortgagePrice);

		int money = getOptionalInt(attrs, "money");
		if ( money != INVALID )
			estate.setMoney(money);

		for (int i = 0; i <= Estate.MAX_HOUSES; i++) {
			int rent = getOptionalInt(attrs, "rent" + i);
			if (rent != INVALID)
				estate.setRent(i, rent);
		}

		int housePrice = getOptionalInt(attrs, "sellhouseprice");
		if ( housePrice != INVALID )
			estate.setHousePrice(housePrice);

		estate.update();
	}

	void dispatchAuctionUpdate(Node node) throws SAXException, InstantiationException {
		NamedNodeMap attrs = node.getAttributes();

		int auctionId = getInt(attrs, "auctionid");

		Auction auction = state.auctions().find(auctionId);
		if( auction == null )
		{
			// Create auction object
			int estateId = getOptionalInt(attrs, "estateid");
			Estate estate = null;
			if( estateId != INVALID ) {
				estate = state.estates().find(estateId);
				if( estate == null )
					estate = state.estates().create(Estate.class, estateId);
			}
			if( estate == null )
				throw new SAXException("Auction contains invalid estate");

			auction = state.auctions().create(Auction.class, auctionId);
			auction.setEstate(estate);
		}

		int highbidderId = getOptionalInt(attrs, "highbidder");
		if( highbidderId != INVALID ) {
			Player player = state.players().find(highbidderId);
			int highbid = getOptionalInt(attrs, "highbid");
			if( highbid != INVALID )
				auction.setHighBid(auction.new Bid(player, highbid));
		}

		int status = getOptionalInt(attrs, "status");
		if( status != INVALID )
			auction.setStatus(Auction.Status.fromInt(status));
		auction.update();

		if(auction.getStatus() == Auction.Status.SOLD)
			state.auctions().remove(auction);
	}

/*
 * else if( checknode(node, "tradeupdate") { a = attrs.getNamedItem("tradeid");
 * if( a == null ) return false; { int tradeId = a.getNodeValue().toInt();
 * 
 * Trade *trade = m_atlanticCore.findTrade(tradeId); if (!trade) { // Create
 * trade object trade = m_atlanticCore.newTrade(tradeId);
 * 
 * QObject::connect(trade, SIGNAL(updateEstate(Trade *, Estate *, Player *)),
 * this, SLOT(tradeUpdateEstate(Trade *, Estate *, Player *)));
 * QObject::connect(trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *,
 * Player *)), this, SLOT(tradeUpdateMoney(Trade *, unsigned int, Player *,
 * Player *))); QObject::connect(trade, SIGNAL(reject(Trade *)), this,
 * SLOT(tradeReject(Trade *))); QObject::connect(trade, SIGNAL(accept(Trade *)),
 * this, SLOT(tradeAccept(Trade *))); }
 * 
 * a = attrs.getNamedItem("revision"); if (trade && !a.isNull())
 * trade.revision(a.getNodeValue().toInt());
 * 
 * QString type = attrs.getNamedItem("type")).getNodeValue(); if (type=="new") { //
 * TODO: trade.actor // Player *player =
 * m_atlanticCore.findPlayer(attrs.getNamedItem("actor")).getNodeValue().toInt()); //
 * if (trade && player) // trade.actor(player);
 * 
 * QDomNode n_player = n.firstChild(); while(!n_player.isNull()) { QDomElement
 * e_player = n_player.toElement(); if (!e_player.isNull() && e_player.tagName() ==
 * "tradeplayer") { Player *player =
 * m_atlanticCore.findPlayer(e_player.attributeNode(QString("playerid")).getNodeValue().toInt());
 * if (trade && player) { trade.addPlayer(player);
 * QObject::connect(m_atlanticCore, SIGNAL(removePlayer(Player *)), trade,
 * SLOT(removePlayer(Player *))); } } n_player = n_player.nextSibling(); } }
 * else if (type=="accepted" && trade) listener.msgTradeUpdateAccepted(trade);
 * else if (type=="completed" && trade) { m_atlanticCore.removeTrade(trade);
 * trade = 0; } else if (type=="rejected") { Player *player =
 * m_atlanticCore.findPlayer(attrs.getNamedItem("actor")).getNodeValue().toInt());
 * if (trade) trade.reject(player); if ( player && player ==
 * m_atlanticCore.playerself ) { m_atlanticCore.removeTrade(trade); trade = 0; } }
 * else { // No type specified, edit is implied.
 * 
 * QDomNode n_child = n.firstChild(); while(!n_child.isNull()) { QDomElement
 * e_child = n_child.toElement(); if (!e_child.isNull()) { if (e_child.tagName() ==
 * "tradeplayer") { a = e_child.attributeNode(QString("playerid")); if( a ==
 * null ) return false; { Player *player =
 * m_atlanticCore.findPlayer(a.getNodeValue().toInt());
 * 
 * a = e_child.attributeNode(QString("accept")); if (trade && player &&
 * !a.isNull()) trade.updateAccept(player, (bool)(a.getNodeValue().toInt())); } }
 * else if (e_child.tagName() == "tradeestate") { a =
 * e_child.attributeNode(QString("estateid")); if( a == null ) return false; {
 * Estate *estate = m_atlanticCore.findEstate(a.getNodeValue().toInt()); a =
 * e_child.attributeNode(QString("targetplayer")); if( a == null ) return false; {
 * Player *player = m_atlanticCore.findPlayer(a.getNodeValue().toInt()); //
 * Allow NULL player, it will remove the component if (trade && estate)
 * trade.updateEstate(estate, player); } } } else if (e_child.tagName() ==
 * "trademoney") { Player *pFrom = 0, *pTo = 0;
 * 
 * a = e_child.attributeNode(QString("playerfrom")); if( a == null ) return
 * false; pFrom = m_atlanticCore.findPlayer(a.getNodeValue().toInt());
 * 
 * a = e_child.attributeNode(QString("playerto")); if( a == null ) return false;
 * pTo = m_atlanticCore.findPlayer(a.getNodeValue().toInt());
 * 
 * a = e_child.attributeNode(QString("money")); kdDebug() < < "tradeupdatemoney" < <
 * (pFrom ? "1" : "0") < < (pTo ? "1" : "0") < < (a.isNull() ? "0" : "1") < <
 * endl; if (trade && pFrom && pTo && !a.isNull())
 * trade.updateMoney(a.getNodeValue().toInt(), pFrom, pTo); } } n_child =
 * n_child.nextSibling(); } }
 * 
 * if (trade) trade.update(); } }
 * 
 */
	private void checkNode(Node node, String name) throws SAXException {
		if( !isNode(node, name) )
			throw new SAXException("Expected " + name + " element");
	}

	private boolean isNode(Node node, String name) {
		return node != null && name.equals(node.getNodeName());
	}

	private Color getOptionalColor(NamedNodeMap attrs, String attName) throws SAXException {
		return getColor(attrs, attName, true);
	}

	private Color getColor(NamedNodeMap attrs, String attName, boolean optional) throws SAXException {
		Color result = null;
		String color = getString(attrs, attName, optional);
		if( color != null && !"".equals(color) ) {
			try {
				result = Color.decode(color);
			} catch( NumberFormatException e ) {
				throw new SAXException("Invalid color");
			}
		}
		return result;
	}
		
	private boolean getBoolean(NamedNodeMap attrs, String attName) throws SAXException {
		return "1".equals(getString(attrs, attName));
	}

	private int getOptionalInt(NamedNodeMap attrs, String attName) throws SAXException {
		return getInt(attrs, attName, true);
	}
	
	private int getInt(NamedNodeMap attrs, String attName) throws SAXException {
		return getInt(attrs, attName, false);
	}
	
	private int getInt(NamedNodeMap attrs, String attName, boolean optional) throws SAXException {
		int result = INVALID;
		String value = getString(attrs, attName, optional);
		
		if( value != null )
			try {
				result = Integer.parseInt(value);
			} catch( NumberFormatException e ) {
				throw new SAXException(e.toString());
			}
		return result;
	}
	
	String getOptionalString(NamedNodeMap attrs, String attName) throws SAXException {
		return getString(attrs, attName, true);
	}
	
	String getString(NamedNodeMap attrs, String attName) throws SAXException {
		return getString(attrs, attName, false);
	}
	
	String getString(NamedNodeMap attrs, String attName, boolean optional) throws SAXException {
		Node a = attrs.getNamedItem(attName);
		
		if( a == null )
			if( optional )
				return null;
			else
				throw new SAXException("Attribute " + attName + " not found");
		
		return a.getNodeValue();
	}

	private Image getImage(String path, String image) {
			Image img = Toolkit.getDefaultToolkit().createImage(path + "/" + image);
			tracker.addImage(img, 0);
			try {
				tracker.waitForAll();
			} catch( InterruptedException e ) {}
			return img;
	}

	public AtlantikListener getListener()
	{
		return listener;
	}

	/**
	 * @return the parser
	 */
	public DocumentBuilder getParser()
	{
		return parser;
	}
	
}
